#include "PageCache.h"

PageCache PageCache::_inst;

PageCache &PageCache::getInstance()
{
    return _inst;
}

Span *PageCache::newSpan(size_t nPage)
{

    // 如果需要的span大于128页，那么直接到堆上去申请
    if (nPage > NPAGES - 1)
    {
        Span *overSpan = _spanPool.alloc();
        void *ptr = systemAlloc(nPage);

        overSpan->_page_id = (PAGE_ID)ptr >> PAGE_SHIFT;
        overSpan->_n = nPage;

        // 将overSpan存入到映射中，在ConcurrentDealloc中需要
        // _pageIdSpanMap[overSpan->_page_id] = overSpan;
        _pageIdSpanMap.set(overSpan->_page_id, overSpan);
        return overSpan;
    }

    // 如果当前pageCache里面有需要的页数的span，就直接返回
    if (!_pageSpanList[nPage].empty())
    {
        Span *retSpan = _pageSpanList[nPage].pop();

        // 如果当前span的页数足够使用，那么就返回当前span，并添加到map映射中
        for (size_t k = 0; k < retSpan->_n; k++)
        {
            // _pageIdSpanMap[retSpan->_page_id + k] = retSpan;
            _pageIdSpanMap.set(retSpan->_page_id + k, retSpan);
        }

        return retSpan;
    }

    // 没有刚好符合大小的span，所以遍历比他大的，将大的span切分为 k 和 span->_n - k 大小的两个span
    size_t k = nPage + 1;
    while (k <= NPAGES - 1)
    {
        if (!_pageSpanList[k].empty())
        {
            Span *nSpan = _pageSpanList[k].pop();
            Span *kSpan = _spanPool.alloc();

            kSpan->_page_id = nSpan->_page_id;
            nSpan->_page_id += nPage;

            kSpan->_n = nPage;
            nSpan->_n -= nPage;

            // 将切分后的span挂载到符合的大小上，需要的span返回
            _pageSpanList[nSpan->_n].pushFront(nSpan);

            // 将nspan的第一页和最后一页添加到map映射中，便于合并span
            // _pageIdSpanMap[nSpan->_page_id] = nSpan;
            // _pageIdSpanMap[nSpan->_page_id + nSpan->_n - 1] = nSpan;
            _pageIdSpanMap.set(nSpan->_page_id, nSpan);
            _pageIdSpanMap.set(nSpan->_page_id + nSpan->_n - 1, nSpan);

            // 将且分出的kSpan所有页，添加到映射中
            for (size_t k = 0; k < kSpan->_n; k++)
            {
                // _pageIdSpanMap[kSpan->_page_id + k] = kSpan;
                _pageIdSpanMap.set(kSpan->_page_id + k, kSpan);
            }

            return kSpan;
        }
        k++;
    }

    // 如果最大的 128 页的span也没有，那么就到堆中去申请
    void *ptr = systemAlloc(NPAGES - 1);

    Span *bigSpan = _spanPool.alloc();
    bigSpan->_n = NPAGES - 1;
    bigSpan->_page_id = (PAGE_ID)ptr >> PAGE_SHIFT;
    _pageSpanList[bigSpan->_n].pushFront(bigSpan);

    return newSpan(nPage);
}

mutex &PageCache::getMtx()
{
    return _mtx;
}

Span *PageCache::pageIdSpanMap(PAGE_ID pageId)
{
    // 就是在这个地方我的基数树还没有实现这个地方不加锁就能进来加锁就进不来了
    // std::unique_lock<mutex> lock(_mtx);
    // unordered_map<PAGE_ID, Span *>::iterator iter = _pageIdSpanMap.find(pageId);
    // if (iter == _pageIdSpanMap.end())
    // {
    //     return nullptr;
    // }
    // return iter->second;
    Span *span = _pageIdSpanMap.get(pageId);

    if (span != nullptr)
    {
        return span;
    }
    else
    {
        return nullptr;
    }
}

void PageCache::dealloc(Span *span)
{
    // 如果当前span的页数大于等于NPAGES的话，直接释放
    if (span->_n > NPAGES - 1)
    {
        systemFree((void *)(span->_page_id << PAGE_SHIFT), span->_n << PAGE_SHIFT);
        _spanPool.dealloc(span);
        return;
    }
    // 找到当前span的前一个页号和后一个页号
    while (1)
    {
        // auto iter = _pageIdSpanMap.find(span->_page_id - 1); // 这个地方可不敢直接调这个接口呢  外面已经加锁了，然后再里面还加锁的话，就是拿着锁要锁了
        // if (iter == _pageIdSpanMap.end())
        // {
        //     break;
        // }
        // Span *preSpan = iter->second;
        Span *preSpan = _pageIdSpanMap.get(span->_page_id - 1);

        // 如果当前span没有前一页或者前一页还在centralCache中使用，或者两个span加一起大于128，就不符合
        if (!preSpan || preSpan->_isUse || preSpan->_n + span->_n > NPAGES - 1)
        {
            break;
        }

        span->_page_id = preSpan->_page_id;
        span->_n += preSpan->_n;
        // 将前一页span 从spanlist中移除
        _pageSpanList[preSpan->_n].erase(preSpan);

        _spanPool.dealloc(preSpan);
    }

    while (1)
    {
        // auto iter = _pageIdSpanMap.find(span->_page_id + span->_n);
        // if (iter == _pageIdSpanMap.end())
        // {
        //     break;
        // }
        // Span *nextSpan = iter->second;
        Span *nextSpan = _pageIdSpanMap.get(span->_page_id + span->_n);

        if (!nextSpan || nextSpan->_isUse || nextSpan->_n + span->_n > NPAGES - 1)
        {
            break;
        }

        span->_n += nextSpan->_n;
        _pageSpanList[nextSpan->_n].erase(nextSpan);

        _spanPool.dealloc(nextSpan);
    }

    span->_isUse = false;
    _pageSpanList[span->_n].pushFront(span);
    // _pageIdSpanMap[span->_page_id] = span;
    // _pageIdSpanMap[span->_page_id + span->_n - 1] = span;
    _pageIdSpanMap.set(span->_page_id, span);
    _pageIdSpanMap.set(span->_page_id + span->_n - 1, span);
}